package jeconbond.economic.market.agent.service;

import jeconbond.economic.resources.IResourceType;
import jeconbond.economic.resources.ResourceUtils;
import jeconbond.economic.systems.market.IMarketResourceRedestributionSystem;
import jeconbond.economic.systems.market.IMoneyAccount;
import jeconbond.economic.systems.market.ISellProposition;
import jeconbond.economic.systems.market.MoneyAccount;
import jeconbond.economic.systems.market.SellProposition;
import jeconbond.economic.market.strategy.IProcessDealStrategy;
import jeconbond.economic.strategy.IMerchant;
import jeconbond.economic.agent.service.IPreActServiceAgent;
import jeconbond.economic.agent.BaseEconomicProducer;

import java.util.Collection;

public class ResellResourceExpensiveSpeculantAgent extends BaseEconomicProducer implements IPreActServiceAgent, IMerchant {
	private static final double INFINIT_MONEY = 1e10;
	
	private IResourceType goalResource;
	private IMarketResourceRedestributionSystem mrrs;
	private IMoneyAccount money = new MoneyAccount(INFINIT_MONEY);
	
	private double greedCost = 0.0;
	private double preSellsMoneySnapshot = money.getBalance();
	private double propCountMarketPart;
	private int dublicateProps;
	private double goalValueMarketPart;

	public ResellResourceExpensiveSpeculantAgent(
			IResourceType goalResource,
			IMarketResourceRedestributionSystem mrrs,
			double propCountMarketPart,
			double goalValueMarketPart
	) {
		this.goalResource = goalResource;
		this.mrrs = mrrs;
		this.merchant = this;
		this.propCountMarketPart = propCountMarketPart;
		this.goalValueMarketPart = goalValueMarketPart;
	}

	@Override
	public void processPreProduceStage() {
		double moneySnapshot = money.getBalance();
		ISellProposition[] propositions = mrrs.getSellPropositionsList(goalResource);
		ResourceUtils.sortSellPropositions(propositions);
		double othersValueOfGoal = computeOthersValueOfGoal();
		double myValueOfGoal = outputRS.howMuchWasStored(goalResource);
		double totalValueOfGoal = othersValueOfGoal + myValueOfGoal;
		double requiredValueOfGoal = totalValueOfGoal * goalValueMarketPart;
		for (ISellProposition sp : propositions) {
			if (requiredValueOfGoal <= 0.0) {
				break;
			}
			if (sp.getSellerMoneyAccount() == money) {
				continue;
			}
			double value = sp.getSellerStorrage().howMuchWasStored(goalResource);
			value = Math.min(value, requiredValueOfGoal);
			requiredValueOfGoal -= value;
			ResourceUtils.processDeal(
					sp,
					value, 
					money, 
					outputRS, 
					mrrs
			);
		}
		double costs = moneySnapshot - money.getBalance();
		greedCost += costs * 2;
		preSellsMoneySnapshot = money.getBalance();
	}

	@Override
	public void processProduceStage() {
		dublicateProps = getRegistredGoalPropsCount();
		dublicateProps = (int) (dublicateProps * propCountMarketPart);
		dublicateProps = Math.max(dublicateProps, 1);
		
		double lastProfit = money.getBalance() - preSellsMoneySnapshot;
		greedCost -= lastProfit;
		greedCost = Math.max(greedCost, 0.0);
		preSellsMoneySnapshot = Double.NaN;
	}
	
	@Override
	public void processPostProduceStage() {
		double wantPrice = greedCost / outputRS.howMuchWasStored(goalResource);
		if (!Double.isNaN(wantPrice) && !Double.isInfinite(wantPrice)) {
			SellProposition sellProposition = new SellProposition(
					goalResource, 
					wantPrice,
					outputRS, 
					money
			);
			for (int i = 0; i < dublicateProps; i++) {
				mrrs.registerProposition(sellProposition);
			}
			dublicateProps = -1;
		}
		money.changeBalance(INFINIT_MONEY - money.getBalance());
	}

	@Override
	public void processPurchase() {}

	@Override
	public void processSelling() {}

	@Override
	public void setProcessDealStrategy(IProcessDealStrategy processDealStrategy) {}

	@Override
	public IMoneyAccount getMoneyAccount() {
		return money;
	}

	private int getRegistredGoalPropsCount() {
		Collection<ISellProposition> allProps = mrrs.getSellPropositionsCollection();
		int result = 0;
		for (ISellProposition p : allProps) {
			if (p.getType() == goalResource) {
				result++;
			}
		}
		return result;
	}

	private double computeOthersValueOfGoal() {
		Collection<ISellProposition> allProps = mrrs.getSellPropositionsCollection();
		double result = 0.0;
		for (ISellProposition p : allProps) {
			if (p.getType() == goalResource) {
				if (p.getSellerMoneyAccount() != money) {
					result += p.getSellerStorrage().howMuchWasStored(goalResource);
				}
			}
		}
		return result;
	}
}
